#include <boost/python.hpp>
#include <string>
#include <iostream>

class  World {
public:
    World(const std::string & msg) : msg(msg) {}
    void set(const std::string & msg) { 
        this->msg = msg;
    }
    std::string greet() { return msg; }

private:
    std::string msg;
};

class Var {
public:
    Var(const std::string & name) 
    : name(name)
    , value()
    , internalObject(new World("internal world"))
    {
    }
    const std::string name;
    float value;
private:
    World * internalObject;
};

class Num {
public:
    float get() const { return value;}
    void set(float value) { this->value = value;}
private:
    float value;
};


class Base {
public:
    Base(const std::string & name) : name(name){}
    virtual ~Base() {}
    std::string get() const  {return name;}
    void set(const std::string &name) { this->name = name;}
private:
    std::string name;
};

class Derived : public Base {
public:
    Derived() : Base("from-cpp default constructor Derived"){}
    virtual ~Derived(){}
};
//Note: same as free_b(Base * base)
void free_func_b(Base & base){
    std::cout << "from cpp:" << base.get() << std::endl;
}
//Note: same as free_d(Derived * derived)
void free_func_d(Derived & derived){
    derived.set("this is d set");
    std::cout << "from cpp:" << derived.get() << std::endl;
}

//Note:create from cpp , owner to python
Base * factory() { return new Derived();}


using namespace boost::python;

BOOST_PYTHON_MODULE(hello){
    class_<World>("World", init<const std::string &>())
        .def("greet", &World::greet)
        .def("set", &World::set)
        ;

    class_<Var>("Var", init<const std::string &>())
        .def_readonly("name", &Var::name)
        .def_readwrite("value", &Var::value)
        .def("newWorld", &Var::new_object, return_value_policy<manage_new_object>())
        .def("internalWorld", &reference_internal, return_value_policy<reference_existing_object>())
        ;

    class_<Num>("Num")
        .add_property("rovalue", &Num::get)
        .add_property("value", &Num::get, &Num::set)
        ;

    class_<Base>("Base", init<const std::string &>())
        .add_property("name", &Base::get, &Base::set)
        ;
    class_<Derived, bases<Base> >("Derived")
        ;

    def("b", free_func_b);
    def("d", free_func_d);

    def("factory", factory, return_value_policy<manage_new_object>());
}
